import {
  WSController,
  OnWSConnection,
  Inject,
  OnWSMessage,
  InjectClient,
} from '@midwayjs/decorator';
import { CsConnService } from '../service/conn';
import { CsSocketTokenMiddleware } from './middleware/token';
import { CachingFactory, MidwayCache } from '@midwayjs/cache-manager';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { GameRoomInfoEntity } from '../entity/room';
/**
 * code-socket
 */
@WSController('/socket')
export class CsSocketIoController {
  @Inject()
  ctx;

  @Inject()
  csConnService: CsConnService;

  @InjectClient(CachingFactory, "default")
  midwayCache: MidwayCache;

  @InjectEntityModel(GameRoomInfoEntity)
  gameRoomInfoEntity: Repository<GameRoomInfoEntity>;

  // 客户端连接
  @OnWSConnection({ middleware: [CsSocketTokenMiddleware] })
  async onConnectionMethod() {
    const { userId } = this.ctx.connData;
    await this.csConnService.binding(userId, this.ctx.id);
  }

  // 发送消息
  @OnWSMessage('send', { middleware: [CsSocketTokenMiddleware] })
  async send(data) {
    const { userId } = this.ctx.connData;
    const { toId, content } = data;
    const { roomId, owner, roomNumber, type } = content;
    // 获得连接ID
    const connId = await this.csConnService.getConnId(toId);
    const key = roomId + '' + roomNumber
    if (type == 'ping') {
      this.ctx.emit('msg', content);
    } else if (type == 'init') {
      await this.midwayCache.del(key + userId)
      // 游戏初始化把房主、成员的游戏信息写入缓存里 
      await this.setMidwayCache(key + userId, content.content)
      let tolist: Array<any> = await this.midwayCache.get(key + toId) || []
      if (tolist.length > 0) {
        let content = {
          type: "ok",
          roomId,
          content: new Date(),
        }
        this.ctx.emit('msg', content);
        this.ctx.to(connId).emit('msg', content);
      }
    } else if (type == 'async') {
      // 同步房间信息,每200毫秒,会把角色、怪物、和数据有变化的道具发过来
      // 读取房主、成员的游戏信息
      let userlist: Array<any> = await this.midwayCache.get(key + userId)
      let tolist: Array<any> = await this.midwayCache.get(key + toId)
      let send_to = []
      if (userlist.find(item => item.type == 'success') || tolist.find(item => item.type == 'success')) {
        console.log('重连发现游戏结束')
        this.ctx.emit('msg', { type: 'success', roomId })
        return
      }
      if (userlist.find(item => item.type == 'fail') || tolist.find(item => item.type == 'fail')) {
        console.log('重连发现游戏结束')
        this.ctx.emit('msg', { type: 'fail', roomId })
        return
      }
      // 合并修改之后的数组列表
      content.content.forEach(item => {
        let index = userlist.findIndex(item1 => item1.node == item.node)
        if (index != -1) {
          userlist[index] = item
        } else {
          userlist.push(item)
        }
        if (tolist.length == 0) return
        let toDetail = tolist.find(item1 => item1.node == item.node)
        if (!toDetail) {
          send_to.push(item)
        } else {
          if (item.socket_number - toDetail.socket_number >= 3) { // 和另一方的数组做比较,如果socket_number比对方的socket_number大4以上,代表对方卡了至少800毫秒,就直接用这次接收的
            send_to.push(item)
            // 通知自己,对方已离线
            this.ctx.emit("msg",{type:'otherOffline',roomId,content:false})
          } else if (item.socket_number - toDetail.socket_number >= 0) { // 如果是在容差范围之内,就判断参数是不是相差过大
            this.ctx.emit("msg",{type:'otherOnline',roomId,content:true})
            if (item.type == 'role') {
              if (item.dizzy != toDetail.dizzy || item.running != toDetail.running || item.direction != toDetail.direction || item.eatName != toDetail.eatName || item.state != toDetail.state || item.eatFood != toDetail.eatFood || Math.abs(item.x - toDetail.x) > 140 || Math.abs(item.y - toDetail.y) > 80) {
                if (item.control_id) {
                  if (item.control_id == userId) { // 只看自己的角色在对方屏幕内是不是和自己有偏差
                    send_to.push(item)
                  }
                } else {
                  send_to.push(item)
                }
              }
            } else if (item.type == 'fraction') {
              // 分数取最大的那个
              if (item.fraction != toDetail.fraction && item.fraction > toDetail.fraction) {
                send_to.push(item)
              }
            } else if (item.type == 'enemy') {
              // 默认使用房主屏幕怪物的坐标
              if (owner) {
                if (item.node.includes('Leaping')) {
                  let { direction, status } = item
                  let { to_direction, to_status } = toDetail
                  if (item.dizzy != toDetail.dizzy || status != to_status || item.dizzy_direction != toDetail.dizzy_direction || item.fly != toDetail.fly || direction != to_direction || item.state != toDetail.state || item.speed != toDetail.speed || Math.abs(item.x - toDetail.x) > 120 || Math.abs(item.y - toDetail.y) > 60) {
                    send_to.push(item)
                  }
                } else {
                  if (item.dizzy != toDetail.dizzy || item.status != toDetail.status || item.dizzy_direction != toDetail.dizzy_direction || item.direction != toDetail.direction || item.avoid != toDetail.avoid || item.state != toDetail.state || item.speed != toDetail.speed || Math.abs(item.x - toDetail.x) > 120 || Math.abs(item.y - toDetail.y) > 60) {
                    send_to.push(item)
                  }
                }
              }
            } else if (item.type == 'property') {
              // 道具没必要实时更新,只需要判断是不是超过一秒没同步就行
            }
          }
          // 否则的话啥也不做,等被另一方同步
        }
      })
      await this.setMidwayCache(key + userId, userlist)
      if (send_to.length > 0) {
        this.ctx.to(connId).emit('msg', { ...content, content: send_to })
      }
    } else if (type == 'changeLevel') {
      this.ctx.to(connId).emit('msg', content);
    } else if (type == 'success' || type == 'fail') {
      // 把房间的成员置空
      await this.gameRoomInfoEntity.update({id:roomId},{memberId:null,memberReady:0,status:0})
      console.log('游戏结束')
      // 通知双方,游戏结束了
      this.ctx.emit('msg', content);
      this.ctx.to(connId).emit('msg', content);
      // 把状态存入双方的数组,如果检测到成功或者失败了,就直接弹出结算页面
      let userlist: Array<any> = await this.midwayCache.get(key + userId)
      let tolist: Array<any> = await this.midwayCache.get(key + toId)
      userlist.push({ type })
      tolist.push({ type })
      await this.setMidwayCache(key + userId, userlist)
      await this.setMidwayCache(key + toId, userlist)
    } else {
      this.ctx.emit('msg', content);
      this.ctx.to(connId).emit('msg', content);
    }
  }
  /**
   * 设置缓存
   * key
   * data
   * time 默认保存十分钟
   */
  async setMidwayCache(key: string, data: any, time: number = 60 * 1000 * 10) {
    await this.midwayCache.set(key, data, time);
  }
}
